﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using Tank.Draw;

namespace Tank.Role
{
    /// <summary>
    /// 坦克/子弹 碰撞
    /// </summary>
    public class TankCollision
    {
        //所有元素坐标为左上角 
        /*判断碰撞原理：
         * 上下左右：获取坦克两个对应的上下左右坐标，判断是否在地图元素中
        */
        /// <summary>
        /// 获取坦克新坐标
        /// </summary>
        /// <param name="dir">坦克方向</param>
        /// <param name="P">坦克坐标（左上角）</param>
        /// <param name="isCollision">是否碰撞</param>
        /// <param name="isNPC">是否为npc 默认值为否</param>
        /// <param name="moveSize">移动大小 默认值为3</param>
        /// <returns></returns>
        public Point getTankPoint(Data.Direction dir, Point P, out bool isCollision, bool isNPC = false, int moveSize = Data.MoveSize)
        {
            isCollision = false;
            switch (dir)
            {
                case Data.Direction.up:
                    P = new Point(P.X, P.Y - moveSize);
                    if (P.Y <= 0)//表示撞墙
                    {
                        P = new Point(P.X, 0);
                        isCollision = true;
                    }
                    //表示碰撞了
                    else if (isMoveCollision(P) || isMoveCollision(getUpRigthPoint(P)))
                    {
                        P = new Point(P.X, Convert.ToInt32((P.Y / Data.GridSize)) * Data.GridSize + Data.GridSize);
                        isCollision = true;
                    }
                    else if (TwoTianCollision(P))//坦克相撞
                    {
                        P = new Point(P.X, P.Y + moveSize);
                        isCollision = true;
                    }
                    break;
                case Data.Direction.down:
                    P = new Point(P.X, P.Y + moveSize);
                    if (P.Y >= Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge))//表示撞墙
                    {
                        P = new Point(P.X, Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge) - 1);
                        isCollision = true;
                    }
                    //表示碰撞了
                    else if (isMoveCollision(getDownLeftPoint(P)) || isMoveCollision(getDownRigthPoint(P)))
                    {
                        P = new Point(P.X, Convert.ToInt32((P.Y / Data.GridSize)) * Data.GridSize + Convert.ToInt32(Data.GridSize * Data.TankEnlarge) - Data.GridSize - 1);
                        isCollision = true;
                    }
                    else if (TwoTianCollision(P))//坦克相撞
                    {
                        P = new Point(P.X, P.Y - moveSize);
                        isCollision = true;
                    }
                    break;
                case Data.Direction.left:
                    P = new Point(P.X - moveSize, P.Y);
                    if (P.X <= 0)//表示撞墙
                    {
                        P = new Point(0, P.Y);
                        isCollision = true;
                    }
                    //表示碰撞了
                    else if (isMoveCollision(P) || isMoveCollision(getDownLeftPoint(P)))
                    {
                        P = new Point(Convert.ToInt32((P.X / Data.GridSize)) * Data.GridSize + Data.GridSize, P.Y);
                        isCollision = true;
                    }
                    else if (TwoTianCollision(P))//坦克相撞
                    {
                        P = new Point(P.X + moveSize, P.Y);
                        isCollision = true;
                    }
                    break;
                case Data.Direction.right:
                    P = new Point(P.X + moveSize, P.Y);
                    if (P.X >= Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge))//表示撞墙
                    {
                        P = new Point(Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge) - 1, P.Y);
                        isCollision = true;
                    }
                    //表示碰撞了
                    else if (isMoveCollision(getUpRigthPoint(P)) || isMoveCollision(getDownRigthPoint(P)))
                    {
                        P = new Point(Convert.ToInt32((P.X / Data.GridSize)) * Data.GridSize + Convert.ToInt32(Data.GridSize * Data.TankEnlarge) - Data.GridSize - 1, P.Y);
                        isCollision = true;
                    }
                    else if (TwoTianCollision(P))//坦克相撞
                    {
                        P = new Point(P.X - moveSize, P.Y);
                        isCollision = true;
                    }
                    break;
            }
            return P;
        }

        #region 获取坦克四角坐标

        /// <summary>
        /// 获取坦克运动后右上角坐标
        /// </summary>
        /// <param name="p">坦克坐标运动后（左上角）</param>
        /// <param name="bullet">子弹计算要除以4</param>
        /// <returns></returns>
        private Point getUpRigthPoint(Point p, int bullet = 1)
        {
            return new Point(p.X + Convert.ToInt32(Data.GridSize * Data.TankEnlarge / bullet), p.Y);
        }

        /// <summary>
        /// 获取坦克运动后左下角坐标
        /// </summary>
        /// <param name="p">坦克坐标运动后（左上角）</param>
        /// <param name="bullet">子弹计算要除以4</param>
        /// <returns></returns>
        private Point getDownLeftPoint(Point p, int bullet = 1)
        {
            return new Point(p.X, p.Y + Convert.ToInt32(Data.GridSize * Data.TankEnlarge / bullet));
        }

        /// <summary>
        /// 获取坦克运动后右下角坐标
        /// </summary>
        /// <param name="p">坦克坐标运动后（左上角）</param>
        /// <param name="bullet">子弹计算要除以4</param>
        /// <returns></returns>
        private Point getDownRigthPoint(Point p, int bullet = 1)
        {
            return new Point(p.X + Convert.ToInt32(Data.GridSize * Data.TankEnlarge / bullet), p.Y + Convert.ToInt32(Data.GridSize * Data.TankEnlarge / bullet));
        }

        #endregion

        #region 判断坦克是否相撞墙

        /// <summary>
        /// 判断运动后的角坐标是否相撞
        /// </summary>
        /// <param name="MovePoint">运动后角坐标</param>
        /// <param name="isDeleteElem">碰撞后是否删除元素</param>
        /// <returns>true 表示相撞</returns>
        private bool isMoveCollision(Point MovePoint, bool isDeleteElem = false)
        {
            Point pt = getElementPoint(MovePoint);
            bool result = isCollision(pt);
            if (isDeleteElem && result)
                removeElement(pt);
            return result;
        }

        /// <summary>
        /// 判断元素是否在地图元素上
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private bool isCollision(Point p)
        {
            if (Map.mapDic.Keys.Contains(p))//地图元素有 则表示碰撞了
                if (Map.mapDic[p] == Data.mapImage.grass)//草地可以践踏
                    return false;
                else
                    return true;
            else
                return false;
        }
        #endregion

        #region 根据坦克坐标获取对应元素左上角坐标
        /// <summary>
        /// 根据坦克坐标获取对应元素坐标
        /// </summary>
        /// <param name="p">坦克坐标</param>
        /// <returns></returns>
        private Point getElementPoint(Point p)
        {
            int x = p.X / Data.GridSize;
            int y = p.Y / Data.GridSize;
            return new Point(x * Data.GridSize, y * Data.GridSize);
        }
        #endregion

        #region 判断两量坦克是否相撞
        /// <summary>
        /// 判断两量坦克是否相撞
        /// </summary>
        /// <param name="p">移动后的坐标</param>
        /// <returns></returns>
        private bool TwoTianCollision(Point p)
        {
            List<Point> p_list = new List<Point>();
            for (int i = 0; i < Data.playList.Count; i++)
            {
                p_list.Add(Data.playList[i].indexPoint);
            }
            for (int i = 0; i < Data.NPCList.Count; i++)
            {
                p_list.Add(Data.NPCList[i].indexPoint);
            }
            p_list = p_list.Where(po => Math.Abs(po.X - p.X) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
                 && Math.Abs(po.Y - p.Y) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
                 ).ToList();
            return p_list.Count <= 1 ? false : true;
        }

        /// <summary>
        /// 判断两个子弹是否相撞
        /// </summary>
        /// <param name="p">移动后的坐标</param>
        /// <param name="indexPt">移动前的坐标</param>
        /// <returns>true 表示相撞</returns>
        private bool TwoBulletCollision(Point p, Point indexPt)
        {
            List<Point> p_list = new List<Point>();
            for (int i = 0; i < Data.BulletList.Count; i++)
            {
                p_list.Add(Data.BulletList[i].indexPoint);
            }
            p_list = p_list.Where(po => Math.Abs(po.X - p.X) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge / 4)
                 && Math.Abs(po.Y - p.Y) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge / 4)
                 && po != indexPt
                 ).ToList();
            bool result = p_list.Count > 0 ? true : false;
            if (result)
            {
                foreach (var item in p_list)
                {
                    removeBullet(item);
                }
            }
            return result;
        }

        /// <summary>
        /// 子弹撞击坦克
        /// </summary>
        /// <param name="p">运动方向</param>
        /// <param name="p">运动后坐标</param>
        /// <param name="BulletType">子弹类型</param>
        /// <returns></returns>
        private bool BulletToTank(Data.Direction dir, Point p, Data.BulletType BulletType)
        {
            bool result = false;
            List<Point> p_list = new List<Point>();
            switch (BulletType)
            {
                case Data.BulletType.NpcBullet://npc子弹打玩家
                    foreach (var item in Data.playList)
                    {
                        p_list.Add(item.indexPoint);
                    }
                    break;
                case Data.BulletType.PlayBullet://玩家子弹打npc
                    foreach (var item in Data.NPCList)
                    {
                        p_list.Add(item.indexPoint);
                    }
                    break;
            }
            switch (dir)
            {
                case Data.Direction.up:
                case Data.Direction.left:
                    p_list = p_list.Where(po => Math.Abs(po.X - p.X) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
                && Math.Abs(po.Y - p.Y) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
                ).ToList();
                    break;
                case Data.Direction.down:
                    p_list = p_list.Where(po => Math.Abs(po.X - p.X) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
               && Math.Abs(po.Y - p.Y) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge / 4)
               ).ToList();
                    break;
                case Data.Direction.right:
                    p_list = p_list.Where(po => Math.Abs(po.X - p.X) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge / 4)
               && Math.Abs(po.Y - p.Y) < Convert.ToInt32(Data.GridSize * Data.TankEnlarge)
               ).ToList();
                    break;
            }
            result = p_list.Count > 0 ? true : false;
            if (result)
            {
                foreach (var item in p_list)
                {
                    removeTank(item, BulletType);
                }
            }

            return result;
        }


        #endregion

        /// <summary>
        /// 获取子弹位移坐标
        /// </summary>
        /// <param name="dir">方向</param>
        /// <param name="P">移动前坐标</param>
        /// <param name="BulletType">子弹类型</param>
        /// <param name="moveSize">移动距离</param>
        /// <returns></returns>
        public Point getBulletPoint(Data.Direction dir, Point P, Data.BulletType BulletType, int moveSize)
        {
            Point indexPoint = P;//移动前坐标
            switch (dir)
            {
                case Data.Direction.up:
                    P = new Point(P.X, P.Y - moveSize);
                    if (P.Y <= 0)//表示撞墙
                    {
                        removeBullet(indexPoint);
                    }
                    //表示碰撞了元素
                    else if (isMoveCollision(P, true) || isMoveCollision(getUpRigthPoint(P, 4), true))
                    {
                        removeBullet(indexPoint);
                    }
                    else if (TwoBulletCollision(P, indexPoint))//子弹相撞
                    {
                        removeBullet(indexPoint);
                    }
                    else if (BulletToTank(dir, P, BulletType))//子弹打坦克
                    {
                        removeBullet(indexPoint);
                    }
                    break;
                case Data.Direction.down:
                    P = new Point(P.X, P.Y + moveSize);
                    //表示撞墙
                    if (P.Y >= Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge))
                    {
                        removeBullet(indexPoint);
                    }
                    //表示碰撞了元素
                    else if (isMoveCollision(getDownLeftPoint(P, 4), true) || isMoveCollision(getDownRigthPoint(P, 4), true))
                    {
                        removeBullet(indexPoint);
                    }
                    else if (TwoBulletCollision(P, indexPoint))//子弹相撞
                    {
                        removeBullet(indexPoint);
                    }
                    else if (BulletToTank(dir, P, BulletType))//子弹打坦克
                    {
                        removeBullet(indexPoint);
                    }
                    break;
                case Data.Direction.left:
                    P = new Point(P.X - moveSize, P.Y);
                    if (P.X <= 0)//表示撞墙
                    {
                        removeBullet(indexPoint);
                    }
                    //表示碰撞了元素
                    else if (isMoveCollision(P, true) || isMoveCollision(getDownLeftPoint(P, 4), true))
                    {
                        removeBullet(indexPoint);
                    }
                    else if (TwoBulletCollision(P, indexPoint))//子弹相撞
                    {
                        removeBullet(indexPoint);
                    }
                    else if (BulletToTank(dir, P, BulletType))//子弹打坦克
                    {
                        removeBullet(indexPoint);
                    }
                    break;
                case Data.Direction.right:
                    P = new Point(P.X + moveSize, P.Y);
                    //表示撞墙
                    if (P.X >= Convert.ToInt32(Data.GridSize * 26 - Data.GridSize * Data.TankEnlarge))
                    {
                        removeBullet(indexPoint);
                    }
                    //表示碰撞了元素
                    else if (isMoveCollision(getUpRigthPoint(P, 4), true) || isMoveCollision(getDownRigthPoint(P, 4), true))
                    {
                        removeBullet(indexPoint);
                    }
                    else if (TwoBulletCollision(P, indexPoint))//子弹相撞
                    {
                        removeBullet(indexPoint);
                    }
                    else if (BulletToTank(dir, P, BulletType))//子弹打坦克
                    {
                        removeBullet(indexPoint);
                    }
                    break;
            }
            return P;
        }

        /// <summary>
        /// 去除子弹
        /// </summary>
        /// <param name="pt">子弹为移动前坐标</param>
        private void removeBullet(Point pt)
        {
            Bullet b = Data.BulletList.Where(p => p.indexPoint == pt).FirstOrDefault();
            if (b != null)
                Data.BulletList.Remove(b);
        }

        /// <summary>
        /// 去除地图元素
        /// </summary>
        /// <param name="p">地图元素位置</param>
        private void removeElement(Point p)
        {
            if (Map.mapDic.Keys.Contains(p))//地图元素有
                if (Map.mapDic[p] == Data.mapImage.wall)//砖墙才可以被消除
                    Map.mapDic.Remove(p);
        }

        /// <summary>
        /// 去除坦克
        /// </summary>
        /// <param name="p">坦克位置</param>
        /// <param name="BulletType">子弹方式</param>
        private void removeTank(Point p, Data.BulletType BulletType)
        {
            switch (BulletType)
            {
                case Data.BulletType.NpcBullet://打玩家
                    Player play = Data.playList.Where(pl => pl.indexPoint == p).FirstOrDefault();
                    if (play != null)
                        Data.playList.Remove(play);
                    break;
                case Data.BulletType.PlayBullet://打npc
                    NPC npc = Data.NPCList.Where(nl => nl.indexPoint == p).FirstOrDefault();
                    if (npc != null)
                    {
                        Data.NPCList.Remove(npc);
                        PlayMusic.blastMusic();
                    }

                    break;
                default:
                    break;
            }
        }
    }
}
